/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.nio;

import java.nio.ByteBuffer;

import org.zhiqim.httpd.HttpContext;
import org.zhiqim.httpd.HttpInputStream;
import org.zhiqim.httpd.websocket.HttpWebSocketAbs;
import org.zhiqim.httpd.websocket.HttpWebSocketService;
import org.zhiqim.kernel.model.maps.HashMapSS;

/**
 * HttpWebsocket连接线程
 *
 * @version v1.0.0 @author zouzhigang 2017-6-9 新建与整理
 */
public class HttpNioWebSocket extends HttpWebSocketAbs
{
    private HttpNioConnection conn;
    private HttpInputStream input;
    private HttpNioWebSocketPacket data;//最后一个消息
    
    //SSL属性
    private boolean hasSsl;
    private HttpNioSslBuffer buffer;
    
    public HttpNioWebSocket(HttpNioConnection conn, HttpContext context, HashMapSS headers, HashMapSS parameters, String protocol, String sessionId, HttpWebSocketService service)
    {
        super(context, headers, parameters, protocol, sessionId, service);
        
        this.conn = conn;
        this.input = new HttpInputStream();
        
        if (conn instanceof HttpNioSslConnection)
        {//对SSL属性初始化
            HttpNioSslConnection sslconn = (HttpNioSslConnection)conn;
            this.hasSsl = true;
            this.buffer = new HttpNioSslBuffer(sslconn.getSSLEngine());
        }
    }
    
    public HttpNioConnection getConnection()
    {
        return conn;
    }
    
    public synchronized void open()
    {
        if (isRunning)
            return;
        
        isRunning = true;
    }
    
    public synchronized void close(int code)
    {
        if (!isRunning)
            return;
        
        isRunning = false;
        if (conn != null)
        {
            conn.close();
            conn = null;
        }
        
        super.close(code);
    }
    
    public synchronized void exception(Throwable e)
    {
        if (!isRunning)
            return;
        
        isRunning = false;
        if (conn != null)
        {
            conn.close();
            conn = null;
        }
        
        super.exception(e);
    }
    
    /***********************************************************************************/
    //异步接受数据
    /***********************************************************************************/
    
    public int execute(ByteBuffer buf)
    {
        int result = buffer(buf);
        if (result <= 0)
        {//NIO返回值为1，NIO-SSL可能返回-1/0/1，针对-1/0的返回连接处理
            return result;
        }
        
        if (data == null || data.isParsed())
            data = new HttpNioWebSocketPacket();
        
        boolean readNext = parse();
        while (readNext && input.available() > 0)
        {
            data = new HttpNioWebSocketPacket();
            readNext = parse();
        }
        
        return 1;
    }
    
    /** 解析缓冲流 */
    private int buffer(ByteBuffer buf)
    {
        int result = 1;
        if (hasSsl)
            result = buffer.parseBuffer(buf, input);
        else
            input.addBuffer(buf);
        return result;
    }
    
    /** 解析数据流 */
    private boolean parse()
    {
        try
        {
            if (!data.parse(input))
            {//未完成解析
                return false;
            }
            
            switch (data.getOpcode())
            {
            case PONG:
            {//心跳响响延续会话
                context.getSessionManager().updateLastAccessed(sessionId);
                service.onPong(this);
                return true;
            }
            case PING:
            {//心跳请求回复响应
                context.getSessionManager().updateLastAccessed(sessionId);
                sendPong();
                service.onPing(this);
                return true;
            }
            case CLOSE:
            {//接收到断开，关闭连接
                close(data.getCloseCode());
                return false;
            }
            case TEXT:
            {//接收到文本消息
                context.getSessionManager().updateLastAccessed(sessionId);
                service.onText(this, data.getText());
                return true;
            }
            case BIN:
            {//接收到二进制流
                context.getSessionManager().updateLastAccessed(sessionId);
                service.onBinary(this, data.getData());
                return true;
            }
            case MID:
            {//接收到中间包
                context.getSessionManager().updateLastAccessed(sessionId);
                service.onMiddle(this, data.getData());
                return true;
            }
            default:
            {//接收到其他消息
                context.getSessionManager().updateLastAccessed(sessionId);
                service.onOther(this, data.getData());
                return true;
            }
            }
        }
        catch (Throwable e)
        {
            exception(e);
            return false;
        }
    }
}
